/**
 * @copyright Copyright (c) 2007-2024, Guangzhou Xaircraft Technology Co., Ltd.
 */
#include "Common.h"       // NOLINT
// #include "xaglog/xaglog.hpp"
// #include "linux/linux_util.h"
#include <sys/sysinfo.h>

#if 0
namespace {
#undef SPDLOG_TRACE
#undef SPDLOG_DEBUG
#undef SPDLOG_INFO
#undef SPDLOG_WARN
#undef SPDLOG_ERROR
#undef SPDLOG_CRITICAL
constexpr char xaglogger[] = "DEAMON";
#define SPDLOG_TRACE(...) XAGLOG_TOPIC_TRACE(xaglogger, ##__VA_ARGS__)
#define SPDLOG_DEBUG(...) XAGLOG_TOPIC_DEBUG(xaglogger, ##__VA_ARGS__)
#define SPDLOG_INFO(...) XAGLOG_TOPIC_INFO(xaglogger, ##__VA_ARGS__)
#define SPDLOG_WARN(...) XAGLOG_TOPIC_WARN(xaglogger, ##__VA_ARGS__)
#define SPDLOG_ERROR(...) XAGLOG_TOPIC_ERROR(xaglogger, ##__VA_ARGS__)
#define SPDLOG_CRITICAL(...) XAGLOG_TOPIC_CRITICAL(xaglogger, ##__VA_ARGS__)
}     // NOLINT
#endif

namespace xag {  // NOLINT
  namespace xos {  // NOLINT
    namespace deamon {  // NOLINT

      void Common::read_uptime(uint64_t *uptime) {
        FILE *fp = NULL;
        char line[128];
        unsigned long up_sec, up_cent;  // NOLINT
        bool err = false;

        if ((fp = fopen(UPTIME, "r")) == NULL) {
          err = true;
        } else if (fgets(line, sizeof(line), fp) == NULL) {
          err = true;
        } else if (sscanf(line, "%lu.%lu", &up_sec, &up_cent) == 2) {
          *uptime = (uint64_t)up_sec * 100 +
                    (uint64_t)up_cent;
        } else {
          err = true;
        }

        if (fp != NULL) {
          fclose(fp);
        }

        if (err) {
          fprintf(stderr, "Cannot read %s\n", UPTIME);
          exit(2);
        }
      }

      void Common::get_kb_shift(void) {
        int shift = 0;
        long size;  // NOLINT

        /* One can also use getpagesize() to get the size of a page */
        if ((size = sysconf(_SC_PAGESIZE)) == -1) {
          perror("sysconf");
        }

        page_size = size;

        size >>= 10; /* Assume that a page has a minimum size of 1 kB */

        while (size > 1) {
          shift++;
          size >>= 1;
        }

        kb_shift = (uint32_t)shift;
      }

      void Common::get_HZ(void) {
        long ticks;  // NOLINT

        if ((ticks = sysconf(_SC_CLK_TCK)) == -1) {
          perror("sysconf");
        }

        // SPDLOG_INFO("ticks {}",ticks);
        printf("ticks = %ld", ticks);

        Common::SYS_HZ = (unsigned long)ticks;  // NOLINT

        // SPDLOG_INFO("Common::SYS_HZ {}",Common::SYS_HZ);
        printf("SYS_HZ = %ld\n", Common::SYS_HZ);
      }

      void Common::get_cpu_num(void) {
        sys_cpu_num = get_nprocs_conf();
        // SPDLOG_INFO("sys_cpu_num {}",sys_cpu_num);
        printf("sys_cpu_num = %d", sys_cpu_num);
      }

      uint64_t Common::get_interval(uint64_t prev_uptime,
                                    uint64_t curr_uptime) {
        uint64_t itv;

        /* prev_time=0 when displaying stats since system startup */
        itv = curr_uptime - prev_uptime;

        if (!itv) {
         /* Paranoia checking */
          itv = 1;
        }

        return itv;
      }

      void Common::add(pid_stats *a, pid_stats *b) {
        a->nvcsw += b->nvcsw;
        a->nivcsw += b->nivcsw;
        a->read_bytes += b->read_bytes;
        a->write_bytes += b->write_bytes;
        a->cancelled_write_bytes += b->cancelled_write_bytes;
        a->threads = b->threads;
      }

      void Common::get_st_pid(st_pid *a, const st_pid *b) {
        a->pid = b->pid;
        a->uid = b->uid;
        a->tgid = b->tgid;
        a->name = b->name;

        for (int i = 0; i < 2; i++) {
          a->pstats[i]->read_bytes = b->pstats[i]->read_bytes;
          a->pstats[i]->write_bytes = b->pstats[i]->write_bytes;
          a->pstats[i]->cancelled_write_bytes = b->pstats[i]->cancelled_write_bytes;
          a->pstats[i]->utime = b->pstats[i]->utime;
          a->pstats[i]->stime = b->pstats[i]->stime;
          a->pstats[i]->gtime = b->pstats[i]->gtime;
          a->pstats[i]->wtime = b->pstats[i]->wtime;
          a->pstats[i]->nvcsw = b->pstats[i]->nvcsw;
          a->pstats[i]->nivcsw = b->pstats[i]->nivcsw;
          a->pstats[i]->threads = b->pstats[i]->threads;
          a->pstats[i]->fd_nr = b->pstats[i]->fd_nr;
        }
      }

      void Common::get_st_pid(st_pid *a, int32_t a_idx, const st_pid *b, int32_t b_idx) {
        a->pid = b->pid;
        a->uid = b->uid;
        a->tgid = b->tgid;
        a->name = b->name;

        a->pstats[a_idx]->read_bytes = b->pstats[b_idx]->read_bytes;
        a->pstats[a_idx]->write_bytes = b->pstats[b_idx]->write_bytes;
        a->pstats[a_idx]->cancelled_write_bytes = b->pstats[b_idx]->cancelled_write_bytes;
        a->pstats[a_idx]->utime = b->pstats[b_idx]->utime;
        a->pstats[a_idx]->stime = b->pstats[b_idx]->stime;
        a->pstats[a_idx]->gtime = b->pstats[b_idx]->gtime;
        a->pstats[a_idx]->wtime = b->pstats[b_idx]->wtime;
        a->pstats[a_idx]->nvcsw = b->pstats[b_idx]->nvcsw;
        a->pstats[a_idx]->nivcsw = b->pstats[b_idx]->nivcsw;
        a->pstats[a_idx]->threads = b->pstats[b_idx]->threads;
        a->pstats[a_idx]->fd_nr = b->pstats[b_idx]->fd_nr;
      }

      void Common::display_info(struct st_pid *plist, int prev, int curr, uint64_t itv) {
        pid_stats *pstc, *pstp;
        if (plist == nullptr) {
          perror("get null");
          return;
        }

        pstc = plist->pstats[curr].get();
        pstp = plist->pstats[prev].get();

        double rbytes = S_VALUE(pstp->read_bytes, pstc->read_bytes, itv) / 1024;
        double wbytes = S_VALUE(pstp->write_bytes, pstc->write_bytes, itv) / 1024;
        double cbytes = S_VALUE(pstp->cancelled_write_bytes, pstc->cancelled_write_bytes, itv) / 1024;
      }

      MemoryInformation GetProgressMemory(const std::string &pid) {
        MemoryInformation mem_info{};
        std::string process_name_address = "/proc/" + pid + "/statm";
        FILE *file_fd = NULL;
        char buf[1024]{0};

        file_fd = ::fopen(process_name_address.c_str(), "r");
        if (file_fd == NULL) {
          ::perror("fopen GetProgressMemory error");
          return mem_info;
        }

        ::fgets(buf, sizeof(buf), file_fd);
        ::sscanf(buf, "%u %u %u", &mem_info.total_memory, &mem_info.physical_memory, &mem_info.shared_memory);

        mem_info.total_memory = mem_info.total_memory * xag::xos::deamon::Common::page_size / 1024 / 1024;
        mem_info.physical_memory = mem_info.physical_memory * xag::xos::deamon::Common::page_size / 1024 / 1024;
        mem_info.shared_memory = mem_info.shared_memory * xag::xos::deamon::Common::page_size / 1024 / 1024;
        ::fclose(file_fd);

        return mem_info;
      }

      ProgramState Common::get_program_state(st_pid *plist,
                                             int prev,
                                             int curr,
                                             uint64_t itv) {
        ProgramState program_state;
        pid_stats *pstc, *pstp;
        if (plist == nullptr) {
          perror("get null");
          return program_state;
        }

        pstc = plist->pstats[curr].get();
        pstp = plist->pstats[prev].get();

        auto mem_info = GetProgressMemory(std::to_string(plist->pid));
        program_state.total_memory = mem_info.total_memory;
        program_state.physical_memory = mem_info.physical_memory;
        program_state.shared_memory = mem_info.shared_memory;

        program_state.name = plist->name;
        program_state.pid = plist->pid;
        program_state.cpu = SP_VALUE(pstp->utime + pstp->stime, pstc->utime + pstc->stime,
                                itv * Common::SYS_HZ / 100) / Common::sys_cpu_num;
        program_state.user_cpu = (pstc->utime - pstc->gtime) < (pstp->utime - pstp->gtime) ? 0.0 :
                    SP_VALUE(pstp->utime - pstp->gtime, pstc->utime - pstc->gtime, itv *
                    Common::SYS_HZ / 100) / Common::sys_cpu_num;
        program_state.system_cpu = SP_VALUE(pstp->stime, pstc->stime, itv *
                                    Common::SYS_HZ / 100) / Common::sys_cpu_num;
        program_state.thread_num = pstp->threads;
        program_state.nvcsw = S_VALUE(pstp->nvcsw, pstc->nvcsw, itv);
        program_state.nivcsw = S_VALUE(pstp->nivcsw, pstc->nivcsw, itv);
        // program_state.rbytes  = S_VALUE(pstp->read_bytes,  pstc->read_bytes, itv) / 1024;
        // program_state.wbytes  = S_VALUE(pstp->write_bytes, pstc->write_bytes, itv) / 1024;
        // program_state.cbytes  = S_VALUE(pstp->cancelled_write_bytes,pstc->cancelled_write_bytes, itv)  / 1024;

        return program_state;
      }
    }  // NOLINT
  }  // NOLINT
}  // NOLINT